home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / pack / ppnew.lha / pp / pp.c < prev    next >
C/C++ Source or Header  |  1991-09-19  |  23KB  |  967 lines

  1. /* pp_patcher v1.3
  2. **
  3. **     PP_Patcher  is  a  small  utility  which enables programs to get at
  4. ** PowerPacked  datafiles,  as  if  these  were  normal  files.   This  is
  5. ** acomplished  by  patching  some  vital  DOS functions, thus redirecting
  6. ** calls to these to my own internal routines.  The overall effect is that
  7. ** PowerPacked  datafiles  appear as normal files.  You can TYPE them in a
  8. ** CLI window, or bring them directly into your favourite editor.  Another
  9. ** good  way  to  use  this  proggy is if you crunch your workbench icons.
  10. ** Workbench  will never know the difference, but it reduces the diskspace
  11. ** occupied  by  icons  by  some  65-70% (usually).  Of course, there is a
  12. ** nominal  performance reduction due to the fact that we have to decrunch
  13. ** the  icons  before  passing them on to Workbench, but this doesn't seem
  14. ** too  annoying.   Especially not if you use optimized (B.A.D.) disks, or
  15. ** increase   the   size   of  the  diskbuffers  (using  the  CLI  command
  16. ** 'Addbuffers' or equivalent).
  17. **
  18. ** For further info, read the DOC file.
  19. **
  20. ** Compiles  under  Aztec V5.0 using large code/large data/32 bit integers
  21. ** Should do fine under Lattice, too, if you fix the #pragma's.
  22. **
  23. ** Freeware 1991, Copyright (C) 1991 by Michael Berg
  24. **                    Sct. Peders Gade 24A, 2th
  25. **                    8900  Randers
  26. **                    DENMARK
  27. */
  28.  
  29. /* That's right -- ALL of these are necessary! */
  30. #include <functions.h>
  31. #include <exec/execbase.h>
  32. #include <exec/nodes.h>
  33. #include <exec/lists.h>
  34. #include <exec/memory.h>
  35. #include <libraries/dos.h>
  36. #include <libraries/dosextens.h>
  37. #include <intuition/intuition.h>
  38. #include <workbench/startup.h>
  39. #include "ppbase.h"            /* enclosed in this directory */
  40.  
  41. /* Comment out the following line if you don't like autodetaching programs! */
  42. #define DETACHED
  43.  
  44. /* Well, the compiler has the inline-code ability -- why not use it? */
  45. #define strlen    _BUILTIN_strlen
  46. #define strcpy    _BUILTIN_strcpy
  47.  
  48. /* Some defines which make the source code look pretty */
  49. typedef struct PPBase PPBASE;
  50.  
  51. /* powerpacker.library related functions */
  52. extern int ppLoadData(char *, int, int, UBYTE **, int *, int (*)());
  53. #pragma amicall(PPBase, 0x1e, ppLoadData(a0,d0,d1,a1,a2,a3))
  54.  
  55. /* Open()-patch related functions */
  56. extern void MakeOpen(void), RestOpen(void);
  57. extern BPTR RealOpen(char *, int);
  58. extern BPTR NewOpen(char *, int);
  59. #pragma regcall(RealOpen(d1,d2))    /* All of these PRAGMAs are EXTREMELY */
  60. #pragma regcall(NewOpen(d1,d2))        /* important !!!!!!!!!!              */
  61.  
  62. /* Close()-patch related functions */
  63. extern void MakeClose(void), RestClose(void);
  64. extern void RealClose(BPTR);
  65. extern void NewClose(BPTR);
  66. #pragma regcall(RealClose(d1))
  67. #pragma regcall(NewClose(d1))
  68.  
  69. /* Examine()-patch related functions */
  70. extern void MakeExamine(void), RestExamine(void);
  71. extern int RealExamine(BPTR, struct FileInfoBlock *);
  72. extern int NewExamine(BPTR, struct FileInfoBlock *);
  73. #pragma regcall(RealExamine(d1,d2))
  74. #pragma regcall(NewExamine(d1,d2))
  75.  
  76. /* Write()-patch related functions */
  77. extern void MakeWrite(void), RestWrite(void);
  78. extern int RealWrite(BPTR, char *, int);
  79. extern int NewWrite(BPTR, char *, int);
  80. #pragma regcall(RealWrite(d1,d2,d3))
  81. #pragma regcall(NewWrite(d1,d2,d3))
  82.  
  83. /* Define the maximum length of a filename and its path.
  84. ** 256 is, as far as I can tell, the AmigaDOS limit (due to BSTR's).
  85. */
  86. #define MAXPATHLEN    256
  87.  
  88. /* Match tags for PowerPacked files */
  89. #define PP20        (('P' << 24) + ('P' << 16) + ('2' << 8) + '0')
  90. #define PX20        (('P' << 24) + ('X' << 16) + ('2' << 8) + '0')
  91.  
  92. /* One of these for each opened, powerpacked file, which has not yet been
  93. ** closed by the Open()'er.
  94. */
  95. struct filenode
  96. {
  97.     struct MinNode mn;
  98.  
  99.     BPTR filehandle;
  100.     char *new_filename, *orig_filename;
  101.     short dirty;
  102. };
  103.  
  104. /* One of these for each caller to NewOpen() */
  105. struct caller
  106. {
  107.     struct MinNode mn;
  108.  
  109.     struct Task *tc;
  110. };
  111.  
  112. /* For inter-process communication */
  113. typedef struct
  114. {
  115.     struct Message Msg;
  116.     /* Additional parameters will live here, eventually */
  117. } MYMSG;
  118.  
  119. /* Global data */
  120. struct MinList templist, callers;
  121. struct Window *win;
  122. struct IntuitionBase *IntuitionBase;
  123. struct GfxBase *GfxBase;
  124. struct PPBase *PPBase;
  125. int patched, wbmode;
  126.  
  127. char temppath[MAXPATHLEN];
  128.  
  129. struct MsgPort *pp_port;
  130. char *pp_portname = "pp.port";
  131.  
  132. #ifdef DETACHED
  133.  
  134. /* Exported to detach module */
  135. int _stack = 4000;
  136. int _priority = 5;
  137. char *_procname = "Powerpacker Patcher";
  138. long _BackGroundIO = 1;                /* We want stdio */
  139. extern BPTR _Backstdout;
  140.  
  141. #else
  142.  
  143. #define _Backstdout Output()
  144.  
  145. #endif
  146.  
  147. /* Misc */
  148. char GBANNER[] = "Powerpacker Patcher V1.1, Copyright (C) 1991, Michael Berg\n";
  149. char WBBANNER[] = "Just double-click on my icon, or do an extended selection \
  150. on a disk or a\ndrawer icon (this tells me where to put temporary files)\n";
  151. char CLIBANNER[] = "Syntax: PP [<temppath>] (temppath defaults to RAM:)\n";
  152. char DEFPATH[] = "RAM:";
  153.  
  154. void climsg(register char *what)
  155. {
  156.     if (_Backstdout)
  157.         Write(_Backstdout,what,strlen(what));
  158. }
  159.  
  160. void wbmsg(register char *what)
  161. {
  162.     register BPTR fh;
  163.  
  164.     if (fh = Open("CON:10/88/620/80/PP Notices:",MODE_NEWFILE))
  165.     {
  166.         char dummy;
  167.  
  168.         Write(fh,what,strlen(what));
  169.         Write(fh,"Press <RETURN>\n",15);
  170.         Read(fh,&dummy,1);
  171.         Close(fh);
  172.     }
  173. }
  174.  
  175. /* Small puts() function (doesn't do a newline, though). If running from
  176. ** CLI, print the message in the CLI window. If running from WB, show the
  177. ** message in a small console window.
  178. */
  179. void Say(register char *what)
  180. {
  181.     if (wbmode)
  182.         wbmsg(what);
  183.     else
  184. #ifndef DETACHED
  185.         climsg(what);
  186. #else
  187.     {
  188.         register char *cr = "\r";
  189.  
  190.         climsg(cr);
  191.         climsg(what);
  192.     }
  193. #endif
  194. }
  195.  
  196. /* CreatePort() should exist in the standard library, but here it is, just
  197. ** in case your library lacks this function (it isn't in ROM)
  198. */
  199. struct MsgPort *myCreatePort(register char *name, register pri)
  200. {
  201.     register UBYTE sigbit;
  202.     register struct MsgPort *gotten;
  203.  
  204.     if
  205.     (
  206.         gotten = (struct MsgPort *)AllocMem
  207.                        (
  208.                         sizeof(*gotten),
  209.                         MEMF_CLEAR | MEMF_PUBLIC
  210.                        )
  211.     )
  212.     {
  213.         if ((sigbit = AllocSignal(-1)) != -1)
  214.         {
  215.             gotten->mp_Node.ln_Name = name;
  216.             gotten->mp_Node.ln_Pri  = pri;
  217.             gotten->mp_Node.ln_Type = NT_MSGPORT;
  218.             gotten->mp_Flags        = PA_SIGNAL;
  219.             gotten->mp_SigBit       = sigbit;
  220.             gotten->mp_SigTask      = FindTask(0);
  221.  
  222.             AddPort(gotten);
  223.  
  224.             return(gotten);
  225.         }
  226.         else
  227.             FreeMem((char *)gotten,sizeof(*gotten));
  228.     }
  229.  
  230.     return(NULL);
  231. }
  232.  
  233. /* DeletePort is also supposed to be in your standard library. Here it is,
  234. ** just in case...
  235. */
  236. void myDeletePort(register struct MsgPort *p)
  237. {
  238.     RemPort(p);
  239.     FreeSignal(p->mp_SigBit);
  240.     FreeMem((char *)p,sizeof(*p));
  241. }
  242.  
  243. /* Universal termination code */
  244. void die(char *errmsg)
  245. {
  246.     /* Print the (optional) error message */
  247.     if (errmsg)
  248.         Say(errmsg);
  249.  
  250.     /* Get rid of our port */
  251.     if (pp_port)
  252.         myDeletePort(pp_port);
  253.  
  254.     /* Restore the original DOS functions. You will not find these
  255.     ** functions in the sourcecode. They are generated by the assembler
  256.     ** macro DOSLibPatch -- see asmsup.c
  257.     */
  258.     if (patched)
  259.     {
  260.         Forbid();
  261.             RestOpen();
  262.             RestClose();
  263.             RestExamine();
  264.             RestWrite();
  265.         Permit();
  266.     }
  267.  
  268.     /* Close libraries */
  269.     if (PPBase) CloseLibrary(PPBase);
  270.  
  271.     /* Finally! */
  272.     exit(errmsg? 20 : 0);
  273. }
  274.  
  275. /* Open up all required libraries */
  276. void openlibs()
  277. {
  278.     if (!(PPBase = (PPBASE *)OpenLibrary("powerpacker.library",0)))
  279.         die("You need powerpacker.library V33+\n");
  280. }
  281.  
  282. /* Install the DOS patches */
  283. void installpatch()
  284. {
  285.     Forbid();
  286.         MakeOpen();
  287.         MakeClose();
  288.         MakeExamine();
  289.         MakeWrite();
  290.     Permit();
  291.  
  292.     patched = 1;
  293. }
  294.  
  295. /* Some Lists need to be initialized */
  296. void initlist()
  297. {
  298.     NewList((struct List *)&templist);
  299.     NewList((struct List *)&callers);
  300. }
  301.  
  302. void doport()
  303. {
  304.     if (!(pp_port = myCreatePort(pp_portname,0)))
  305.         die("Couldn't create a message port\n");
  306. }
  307.  
  308. /* passargs passes messages to the 'PP' already running somewhere */
  309. void passargs(register ac, register char **av)
  310. {
  311.     register MYMSG *m;
  312.  
  313.     if (m = (MYMSG *)AllocMem(sizeof(MYMSG),MEMF_CLEAR))
  314.     {
  315.         register struct Process *myself;
  316.  
  317.         myself = (struct Process *)FindTask(0);
  318.  
  319.         m->Msg.mn_Node.ln_Type    = NT_MESSAGE;
  320.         m->Msg.mn_Length    = sizeof(MYMSG);
  321.         m->Msg.mn_ReplyPort    = &myself->pr_MsgPort;
  322.  
  323.         /* Passing of future parameters go here */
  324.  
  325.         /* Tell him the bad news */
  326.         PutMsg(pp_port,(struct Message *)m);
  327.  
  328.         /* Receiver will free the message (he MUST NOT reply to it!) */
  329.     }
  330.     else
  331.         Say("Not enough memory for interprocess communication\n");
  332. }
  333.  
  334. void finalizepath()
  335. {
  336.     register char c;
  337.  
  338.     c = temppath[strlen(temppath)-1];
  339.     if (c != ':' && c != '/')
  340.         strcat(temppath,"/");
  341. }
  342.  
  343. void badstartup()
  344. {
  345.     die(wbmode? WBBANNER : CLIBANNER);
  346. }
  347.  
  348. void checkokpath()
  349. {
  350.     register BPTR lock;
  351.     register char *c;
  352.     register allok = 0;
  353.     char testdir[MAXPATHLEN];
  354.  
  355.     strcpy(testdir,temppath);
  356.     c = testdir + strlen(testdir) - 1;
  357.     if (*c == '/') *c = '\0';
  358.  
  359.     if (lock = Lock(testdir,ACCESS_READ))
  360.     {
  361.         register struct FileInfoBlock *fib;
  362.  
  363.         if (fib = AllocMem(sizeof(*fib),MEMF_CLEAR))
  364.         {
  365.             if (Examine(lock,fib))
  366.                 allok = (fib->fib_DirEntryType >= 0);
  367.  
  368.             FreeMem(fib,sizeof(*fib));
  369.         }
  370.         UnLock(lock);
  371.     }
  372.  
  373.     if (!allok)
  374.         die("Could not validate your path selection\n");
  375. }
  376.  
  377. void buildfromlock(register BPTR lock, register char *name)
  378. {
  379.     register struct FileInfoBlock *fib;
  380.     register BPTR olddir,newlock;
  381.     char *fullname();
  382.  
  383.     olddir  = CurrentDir(lock);
  384.  
  385.     if (!(newlock = Lock(name,ACCESS_READ)))
  386.     {
  387.         CurrentDir(olddir);
  388.         die("Could not get a lock on your specified PATH\n");
  389.     }
  390.  
  391.     if (fib = AllocMem(sizeof(*fib),MEMF_CLEAR))
  392.     {
  393.         register retval;
  394.  
  395.         if (retval = Examine(newlock,fib))
  396.             strcpy(temppath,fullname(newlock,fib));
  397.  
  398.         UnLock(newlock);
  399.         CurrentDir(olddir);
  400.         FreeMem(fib,sizeof(*fib));
  401.  
  402.         if (!retval)
  403.             die("Cannot examine your PATH specification\n");
  404.     }
  405.     else
  406.     {
  407.         UnLock(newlock);
  408.         die("Running low on memory\n");
  409.     }
  410. }
  411.  
  412. /* Executed when PP starts up the very first time */
  413. void doinitargs(register ac, register char **av)
  414. {
  415.     register char c;
  416.     register struct WBArg *arg;
  417.     char debugstr[40];
  418.     extern struct WBStartup *WBenchMsg;
  419.  
  420.     /* So far, the only thing you can tell PP is where to put all
  421.     ** the temporary files. This defaults to RAM: when no argument
  422.     ** is given. Workbench argument passing is fully supported.
  423.     */
  424.     switch (ac)
  425.     {
  426.         case 0 : /* Workbench */
  427.              switch (WBenchMsg->sm_NumArgs)
  428.              {
  429.                  case 1 : strcpy(temppath,DEFPATH);
  430.                      break;
  431.  
  432.                 case 2 : arg = &WBenchMsg->sm_ArgList[1];
  433.                      if (arg->wa_Lock)
  434.                          buildfromlock(arg->wa_Lock,arg->wa_Name);
  435.                      else
  436.                         /* Should never happen */
  437.                          strcpy(temppath,arg->wa_Name);
  438.                      break;
  439.  
  440.                 default: badstartup();
  441.              }
  442.              break;
  443.  
  444.         case 1 : strcpy(temppath,DEFPATH);
  445.              break;
  446.  
  447.         case 2 : strcpy(temppath,av[1]);
  448.              break;
  449.  
  450.         default: badstartup();
  451.     }
  452.  
  453.      finalizepath();
  454.     checkokpath();
  455. }
  456.  
  457. /* Open up everything */
  458. void openstuff(register ac, register char **av)
  459. {
  460.     if (pp_port = FindPort(pp_portname))
  461.     {
  462.         /* There's already a working copy of PP running somewhere.
  463.         ** Pass the arguments along to it, and then exit.
  464.         */
  465.         passargs(ac,av);
  466.  
  467.         /* Don't let die() remove the port */
  468.         pp_port = NULL;
  469.         die(NULL);
  470.     }
  471.  
  472.     /* This is the first time around. Install everything */
  473.     doinitargs(ac,av);
  474.  
  475.     openlibs();
  476.     initlist();
  477.     installpatch();
  478.     doport();
  479.  
  480.     /* Tell the world the good news */
  481.     Say(GBANNER);
  482. }
  483.  
  484. /* Add a new caller to NewOpen() to the list of callers */
  485. addcaller(register struct Task *tc)
  486. {
  487.     register struct caller *memgot;
  488.  
  489.     if (memgot = AllocMem(sizeof(*memgot),0))
  490.     {
  491.         memgot->tc = tc;
  492.         AddTail((struct List *)&callers, (struct Node *)memgot);
  493.         return(1);
  494.     }
  495.     else
  496.         return(0);
  497. }
  498.  
  499. /* Add a file node to the list of files which we have created. These
  500. ** exist temporarily in 'temppath' and we need to get rid of these along
  501. ** the way, as they are Close()'d.
  502. */
  503. addfilenode(register BPTR fn, register char *filename, register char *orig)
  504. {
  505.     register struct filenode *memgot;
  506.  
  507.     if
  508.     (
  509.         (memgot = AllocMem(sizeof(*memgot),MEMF_CLEAR))         &&
  510.         (memgot->new_filename  = AllocMem(strlen(filename)+1,0)) &&
  511.         (memgot->orig_filename = AllocMem(strlen(orig)+1,0))
  512.     )
  513.     {
  514.         memgot->filehandle = fn;
  515.         strcpy(memgot->new_filename,filename);
  516.         strcpy(memgot->orig_filename,orig);
  517.         AddTail((struct List *)&templist, (struct Node *)memgot);
  518.         return(1);
  519.     }
  520.     else
  521.         return(0);
  522. }
  523.  
  524. /* Find a filenode (keyed by its filehandle) */
  525. struct filenode *findfilenode(register BPTR fn)
  526. {
  527.     register struct filenode *search;
  528.  
  529.     /* Linear search is employed */
  530.     for
  531.     (
  532.         search = (struct filenode *)(templist.mlh_Head);
  533.         search->mn.mln_Succ;
  534.         search = (struct filenode *)(search->mn.mln_Succ)
  535.     )
  536.         if (search->filehandle == fn)
  537.             return(search);
  538.  
  539.     return(NULL);
  540. }
  541.  
  542. /* Find a caller on the callers list */
  543. struct caller *findcaller(register struct Task *tc)
  544. {
  545.     register struct caller *search;
  546.  
  547.     for
  548.     (
  549.         search = (struct caller *)(callers.mlh_Head);
  550.         search->mn.mln_Succ;
  551.         search = (struct caller *)(search->mn.mln_Succ)
  552.     )
  553.         if (search->tc == tc)
  554.             return(search);
  555.  
  556.     return(NULL);
  557. }
  558.  
  559. /* This baby builds a complete filename (including a path) from a BCPL
  560. ** pointer to a filehandle. Optimizations are most welcome. All those
  561. ** ParentDir() calls take a LONG time.
  562. */
  563. char *fullname(register BPTR lock, register struct FileInfoBlock *fib)
  564. {
  565.     static char pathandfile[MAXPATHLEN];
  566.     char tmp[MAXPATHLEN];
  567.     register BPTR parentlock, unlocklock;
  568.     register char *co;
  569.  
  570.     strcpy(pathandfile,fib->fib_FileName);
  571.  
  572.     parentlock = lock;
  573.     unlocklock = (BPTR)0;
  574.  
  575.     while (parentlock = ParentDir(parentlock))
  576.     {
  577.         if (unlocklock)
  578.             UnLock(unlocklock);
  579.  
  580.         if (patched? RealExamine(parentlock,fib) : Examine(parentlock,fib))
  581.         {
  582.             strcpy(tmp,fib->fib_FileName);
  583.             strcat(tmp,"/");
  584.             strcat(tmp,pathandfile);
  585.             strcpy(pathandfile,tmp);
  586.         }
  587.         else
  588.         {
  589.             UnLock(parentlock);
  590.             return(NULL);
  591.         }
  592.  
  593.         unlocklock = parentlock;
  594.     }
  595.  
  596.     if (unlocklock)
  597.     {
  598.         UnLock(unlocklock);
  599.  
  600.         if (co = (char *)index(pathandfile,'/'))
  601.             *co = ':';
  602.     }
  603.     else
  604.         strcat(pathandfile,":");
  605.  
  606.     /* This fixes a bug in the old RAM disk */
  607.     if (!strcmp(pathandfile,":"))
  608.         strcpy(pathandfile,"RAM:");
  609.  
  610.     return(pathandfile);
  611. }
  612.  
  613. /* Is a filename really a file? We need to know this, because it is
  614. ** rediculous to try to read in the PowerPacker matchtag from something
  615. ** like CON:0/0/544/23/ConWindow.
  616. */
  617. reallyfile(register char *filename)
  618. {
  619.     /* Don't like to hard-wire it like this, but it seems to
  620.     ** be the only realistic approach. Don't worry, you can't
  621.     ** do an ASSIGN to any of these. If you could, we would have
  622.     ** to check all volumenodes on the DeviceList stored in
  623.     ** the RootNode of the DosLibrary. (Got that?!)
  624.     */
  625.     static char *duds[] =
  626.     {
  627.         "NIL:",    "CON:",
  628.         "RAW:",    "PRT:",
  629.         "PAR:",    "SER:"
  630.     };
  631.  
  632.     register short i;
  633.  
  634.     /* A simple, linear search is employed */
  635.     for (i = 0; i < sizeof(duds)/sizeof(char *); i++)
  636.     {
  637.         register short foundit;
  638.         register char *cmp, *cmp2;
  639.         register short j;
  640.  
  641.         cmp  = duds[i];
  642.         cmp2 = filename;
  643.  
  644.         for (foundit = 1, j = 0; j < 4; j++)
  645.         {
  646.             if (toupper(*cmp++) != toupper(*cmp2++))
  647.             {
  648.                 foundit = 0;
  649.                 break;
  650.             }
  651.         }
  652.  
  653.         if (foundit)
  654.             return(0);
  655.     }
  656.  
  657.     /* One last check, just to be sure */
  658.     if (!strcmp(filename,"*"))
  659.         return(0);
  660.  
  661.     return(1);
  662. }
  663.  
  664. /* When somebody opens a PP file, we decrunch it into a temporary file
  665. ** and return a filehandle to that file. When the caller closes the file
  666. ** (which it thinks is the original disk file), it will really be closing
  667. ** the temporary file. This is a good chance for us to get rid of it,
  668. ** so that the temporary directory won't get crowded in time. HOWEVER!
  669. ** If the caller has written new data into the file, we have to rewrite the
  670. ** temporary file over the original (disk) file. flushout() does exactly
  671. ** that.
  672. */
  673. void flushout(register struct filenode *fn)
  674. {
  675.     register BPTR orighandle;
  676.  
  677.     /* First of all, we have to open the original file. We're in
  678.     ** trouble if this is not possible...
  679.     */
  680.     if (orighandle = RealOpen(fn->orig_filename, MODE_NEWFILE))
  681.     {
  682.         char buffer[2048];        /* Should suffice */
  683.         register short readlen;
  684.  
  685.         Seek(fn->filehandle, 0, -1);
  686.  
  687.         do
  688.         {
  689.             readlen = Read(fn->filehandle, buffer, 2048);
  690.             RealWrite(orighandle, buffer, readlen);
  691.         }
  692.             while (readlen == 2048);
  693.  
  694.         RealClose(orighandle);
  695.     }
  696. }
  697.  
  698. /* Look for a powerpacker matchtag at the beginning of a file. Returns TRUE
  699. ** if the file was a powerpacker datafile.
  700. */
  701. isppfile(register BPTR fh)
  702. {
  703.     int ppmatchtag;
  704.  
  705.     /* This function leaves the file position ptr. at 0 for non-PP files */
  706.     Seek(fh,0,-1);
  707.     Read(fh,(char *)&ppmatchtag,sizeof(int));
  708.  
  709.     if (ppmatchtag == PP20 || ppmatchtag == PX20)
  710.         return(1);
  711.     else
  712.     {
  713.         Seek(fh,0,-1);
  714.         return(0);
  715.     }
  716. }
  717.  
  718. /* This is the new Open() functions. All future calls to the DOS Open()
  719. ** function will be rerouted through here.
  720. */
  721. BPTR NewOpen(register char *filename, register mode)
  722. {
  723.     UBYTE *memgot;
  724.     int filelen;
  725.     register BPTR tempfh;
  726.     register struct Task *thistask;
  727.     register struct caller *thiscaller;
  728.  
  729.     tempfh = RealOpen(filename,mode);
  730.  
  731.     /* We only deal with a few of the incoming calls:
  732.     **
  733.     ** 1) Files which CAN in fact be opened
  734.     **
  735.     ** 2) We can't do anything about new files
  736.     **
  737.     ** 3) Equally, we don't care about CON: or NIL: file open requests
  738.     **
  739.     ** 4) We don't care about non-crunched files
  740.     **
  741.     ** 5) If we have seen the calling task before, the one who is making
  742.     **    the request must be ppLoadData. It is absolutely vital that
  743.     **    we forward this request to the original DOS code. Otherwise
  744.     **    we would end up in an infinite (recursive) loop, with ppLoadData
  745.     **    calling NewOpen calling ppLoadData ...
  746.     **
  747.     ** 6) If we cannot add a caller to the list of callers (see 3), we
  748.     **    ignore the call. If we miss a few in a low memory situation,
  749.     **    so be it.
  750.     */
  751.  
  752.     if
  753.     (
  754.         !tempfh                   ||
  755.         mode == MODE_NEWFILE           ||
  756.         !reallyfile(filename)           ||
  757.         !isppfile(tempfh)           ||
  758.         findcaller(thistask = FindTask(0)) ||
  759.         !addcaller(thistask)
  760.     )
  761.         return(tempfh);
  762.  
  763.     /* We won't be needing the original file handle anymore */
  764.     RealClose(tempfh);
  765.     tempfh = (BPTR)0;
  766.  
  767.     /* Now, ask ppLoadData to bring in the file */
  768.     if (!ppLoadData(filename,DECR_NONE,0,&memgot,&filelen,(int (*)())0))
  769.     {
  770.         char filnambuf[MAXPATHLEN];
  771.         register char *t, *m;
  772.  
  773.         /* Generate a name for the temporary file */
  774.         t = filename;
  775.         if (m = (char *)index(t,':')) t = m+1;
  776.         while (m = (char *)index(t,'/')) t = m+1;
  777.  
  778.         strcpy(filnambuf,temppath);
  779.         strcat(filnambuf,t);
  780.         strcat(filnambuf,".tmp");
  781.  
  782.         /* We have to ensure that the name is unique on 'temppath' */
  783.         while (tempfh = RealOpen(filnambuf,MODE_OLDFILE))
  784.         {
  785.             char *xtra = "?";
  786.  
  787.             /* Pad the name with random characters. This
  788.             ** should do the trick
  789.             */
  790.             RealClose(tempfh);
  791.             *xtra = 'A' + (rand() % 26);
  792.             strcat(filnambuf,xtra);
  793.         }
  794.  
  795.         /* Now, open the temporary file and flush data we loaded into
  796.         ** this file.
  797.         */
  798.         if (tempfh = RealOpen(filnambuf,MODE_NEWFILE))
  799.         {
  800.             /* Remember that WE created that file */
  801.             if (!addfilenode(tempfh,filnambuf,filename))
  802.             {
  803.                 /* Couln't do it. Simulate a "Can't open
  804.                 ** file" from the real Open().
  805.                 */
  806.                 RealClose(tempfh);
  807.                 DeleteFile(filnambuf);
  808.                 tempfh = (BPTR)0;
  809.             }
  810.             else
  811.             {
  812.                 /* Flush out the file. Probably should do
  813.                 ** a check on RealWrite... Oh well.
  814.                 */
  815.                 RealWrite(tempfh,(char *)memgot,filelen);
  816.                 Seek(tempfh,0,-1);
  817.             }
  818.         }
  819.         /* Housekeeping */
  820.         FreeMem(memgot,filelen);
  821.     }
  822.  
  823.     /* We no longer have to worry about this caller */
  824.     thiscaller = findcaller(thistask);
  825.     Remove((struct Node *)thiscaller);
  826.     FreeMem(thiscaller, sizeof(*thiscaller));
  827.  
  828.     /* Return a filehandle to the file */
  829.     return(tempfh);
  830. }
  831.  
  832. /* Yep! A new Write() function. We have to know if a process has updated
  833. ** the (substitute) file we created for it. If true, mark the file as
  834. ** being "dirty", so that we can later save it over the original PP file.
  835. */
  836. int NewWrite(register BPTR filehandle, register char *buffer, int length)
  837. {
  838.     register struct filenode *fn;
  839.     register wrtret;
  840.  
  841.     wrtret = RealWrite(filehandle, buffer, length);
  842.  
  843.     if (fn = findfilenode(filehandle))
  844.         fn->dirty = 1;
  845.  
  846.     return(wrtret);
  847. }
  848.  
  849. /* A new Close() function. It removes non-dirty, temporary files from
  850. ** 'temppath', and it keeps track of which files have to be updated back
  851. ** onto disk.
  852. */
  853. void NewClose(register BPTR filehandle)
  854. {
  855.     register struct filenode *fn;
  856.  
  857.     if (fn = findfilenode(filehandle))
  858.     {
  859.         if (fn->dirty)
  860.             flushout(fn);
  861.  
  862.         RealClose(filehandle);
  863.         DeleteFile(fn->new_filename);
  864.         Remove((struct Node *)fn);
  865.         FreeMem(fn,sizeof(*fn));
  866.     }
  867.     else
  868.         RealClose(filehandle);
  869. }
  870.  
  871. /* A new Examine() function. Often, programs examine a file before opening
  872. ** it. This way, they can allocate just enough memory to hold the entire
  873. ** file. However, we have to correct Examine() calls to PowerPacked files,
  874. ** so that the correct amount of memory will be allocated by the caller.
  875. */
  876. int NewExamine(BPTR lock, struct FileInfoBlock *fib)
  877. {
  878.     int decrunchinfo;
  879.     register examinereturn;
  880.     register BPTR tmpfh;
  881.     struct FileInfoBlock fib_backup;
  882.  
  883.     /* Start off by examining the lock */
  884.     if (!(examinereturn = RealExamine(lock,fib)))
  885.         return(0);
  886.  
  887.     /* If it's a directory, or if it's pp_LoadData, never mind */
  888.     if (fib->fib_DirEntryType >= 0 || findcaller(FindTask(0)))
  889.         return(examinereturn);
  890.  
  891.     /* The lock target was a simple file. Check to see if it's a
  892.     ** PP file
  893.     */
  894.     fib_backup = *fib;
  895.     tmpfh       = RealOpen(fullname(lock,fib),MODE_OLDFILE);
  896.     *fib       = fib_backup;
  897.  
  898.     if (!tmpfh)
  899.         return(examinereturn);
  900.  
  901.     if (isppfile(tmpfh))
  902.     {
  903.         /* It was. Examine decrunchinfo to get at the original
  904.         ** filesize, so that programs trying to allocate enough
  905.         ** memory to hold a certain file will get the correct
  906.         ** filesize (which is, of corz, size of the decrunched
  907.         ** file!)
  908.         */
  909.         Seek(tmpfh,-4,1);
  910.         Read(tmpfh,(char *)&decrunchinfo,sizeof(int));
  911.         fib->fib_Size = decrunchinfo >> 8;
  912.     }
  913.  
  914.     RealClose(tmpfh);
  915.  
  916.     return(examinereturn);
  917. }
  918.  
  919. maydie()
  920. {
  921.     if (callers.mlh_Head->mln_Succ)
  922.         return(0);
  923.     else
  924.         return(1);
  925. }
  926.  
  927. /* Hangaround() waits for messages to arrive at our port, and then deals
  928. ** with them. This routine could be written in a much smaller version, but
  929. ** I've written it so that it will be easy to add more 'commands'.
  930. */
  931. hangaround()
  932. {
  933.     register MYMSG *m;
  934.  
  935.     FOREVER
  936.     {
  937.         WaitPort(pp_port);
  938.  
  939.         while (m = (MYMSG *)GetMsg(pp_port))
  940.         {
  941.             /* Eventually, some kind of actual communication
  942.             ** will take place here. In this version, we simply
  943.             ** quit whenever we spot an incomming message.
  944.             */
  945.  
  946.             /* Note! MUST NOT reply! (Caller may already have
  947.             ** terminated, since he doesn't WaitPort())
  948.             */
  949.             FreeMem(m,sizeof(*m));
  950.  
  951.             if (maydie())
  952.                 die("PP V1.1: Terminated.\n");
  953.             else
  954.                 Say("PP V1.1: Can't terminate just yet.\n");
  955.         }
  956.     }
  957. }
  958.  
  959. /* Entry point */
  960. void main(int argc, char *argv[])
  961. {
  962.     wbmode = (argc == 0);
  963.  
  964.     openstuff(argc,argv);
  965.     hangaround();            /* Never returns */
  966. }
  967.